﻿Imports System.Management

''' <summary>
''' This class represents a WMI collector.
''' </summary>
''' <remarks>
''' The purpose of the WMI collector is to run WMI queries against the system and
''' gather specific information to include in the system info report.
''' </remarks>
Public Class WMICollector
    Implements ICollector

    ''' <summary>
    ''' List of writers passed when Startup() is called.
    ''' </summary>
    ''' <remarks></remarks>
    Protected m_writers As List(Of Writer.IWriter)

    ''' <summary>
    ''' List of WMI entries.
    ''' </summary>
    ''' <remarks></remarks>
    Protected m_entries As List(Of WMIEntry)

    ''' <summary>
    ''' This holds an error description.
    ''' </summary>
    ''' <remarks></remarks>
    Protected m_error As String

    Public Sub Cleanup() Implements ICollector.Cleanup

        'Nothing to do

    End Sub

    Public Sub Run() Implements ICollector.Run

        'Write our category name.
        Writer.WritersHelper.WriteCategory("System Metrics", m_writers)

        'If an error occurred while reading the configuration file, write
        'it out and stop here.
        If m_error <> "" Then
            Writer.WritersHelper.WriteCategoryError(m_error, m_writers)
        Else
            'Scan all mssql entries.
            For Each wmi As WMIEntry In m_entries
                Try
                    'Write the database server and database name.
                    Writer.WritersHelper.WriteSection(wmi.Name, m_writers)
                    'Process this item.
                    Try
                        Dim search As New ManagementObjectSearcher(wmi.WMIQuery)
                        Dim info As ManagementObject
                        For Each info In search.Get()
                            'Try to find metrics specified
                            For i As Integer = 0 To wmi.Metrics.Count - 1
                                Dim metStr As String = ""
                                Dim valStr As String = "<not available>"
                                Try
                                    Dim met As WMIMetric = CType(wmi.Metrics(i), WMIMetric)
                                    metStr = met.Name
                                    valStr = info(met.Name).ToString()
                                    'Cater for lookups
                                    If met.Lookups.Count > 0 Then
                                        Dim l As WMILookup = met.GetLookup(valStr)
                                        If l Is Nothing Then
                                            valStr = valStr + " (UNKNOWN)"
                                        Else
                                            valStr = l.Description + " (" + valStr + ")"
                                        End If
                                    End If
                                Catch ex As Exception
                                End Try
                                Writer.WritersHelper.WriteEntry(metStr, valStr, m_writers)
                            Next
                            Writer.WritersHelper.WriteBlankEntry(m_writers)
                        Next
                    Catch ex As Exception
                        'Error gathering info for this one.
                        Writer.WritersHelper.WriteEntryError("Error retrieving WMI information (" + ex.Message + ")", m_writers)
                    End Try
                Catch ex As Exception
                    'Error gathering info.
                    Writer.WritersHelper.WriteSectionError("Error retrieving WMI information (" + ex.Message + ")", m_writers)
                End Try
                'Write out a blank.
                Writer.WritersHelper.WriteBlank(m_writers)
            Next
        End If

    End Sub

    Public Sub Startup(ByVal cfgFile As String, ByVal writers As System.Collections.Generic.List(Of Writer.IWriter)) Implements ICollector.Startup

        'Get all writers.
        m_writers = writers
        'Read configuration file.
        ReadConfiguration(cfgFile)

    End Sub

    ''' <summary>
    ''' Read the configuration file.
    ''' </summary>
    ''' <param name="fName">File name with MSSQL collector configuration.</param>
    ''' <remarks></remarks>
    Protected Sub ReadConfiguration(ByVal fName As String)

        Const SYSTEMMETRIC_ENTRY As String = "SystemMetric"
        Const METRICS_ENTRY As String = "Metrics"
        Const SYSTEMMETRIC_ID As String = "SystemMetric_id"
        Const METRIC_ENTRY As String = "Metric"
        Const METRICS_ID As String = "Metrics_id"
        Const METRIC_ID As String = "metric_id"
        Const LOOKUPS_ID As String = "lookups_id"
        Const LOOKUPS_ENTRY As String = "Lookups"
        Const LOOKUP_ENTRY As String = "Lookup"

        Try
            'Read in a dataset.
            Dim ds As New DataSet
            ds.ReadXml(fName)
            m_entries = New List(Of WMIEntry)

            For i As Integer = 0 To ds.Tables(SYSTEMMETRIC_ENTRY).Rows.Count - 1
                Dim sysMet As New WMIEntry(Convert.ToString(ds.Tables(SYSTEMMETRIC_ENTRY).Rows(i).Item("Name")), _
                                           Convert.ToString(ds.Tables(SYSTEMMETRIC_ENTRY).Rows(i).Item("WMIQuery")))
                Dim systemMetricID As Integer = Convert.ToInt32(ds.Tables(SYSTEMMETRIC_ENTRY).Rows(i).Item(SYSTEMMETRIC_ID))
                For j As Integer = 0 To ds.Tables(METRICS_ENTRY).Rows.Count - 1
                    If Convert.ToInt32(ds.Tables(METRICS_ENTRY).Rows(j).Item(SYSTEMMETRIC_ID)) = systemMetricID Then
                        Dim metricsID As Integer = Convert.ToInt32(ds.Tables(METRICS_ENTRY).Rows(j).Item(METRICS_ID))
                        For z As Integer = 0 To ds.Tables(METRIC_ENTRY).Rows.Count - 1
                            If Convert.ToInt32(ds.Tables(METRIC_ENTRY).Rows(z).Item(METRICS_ID)) = metricsID Then
                                Dim metObj As New WMIMetric(Convert.ToString(ds.Tables(METRIC_ENTRY).Rows(z).Item("Name")))
                                Dim metricID As Integer = Convert.ToInt32(ds.Tables(METRIC_ENTRY).Rows(z).Item(METRIC_ID))
                                For l As Integer = 0 To ds.Tables(LOOKUPS_ENTRY).Rows.Count - 1
                                    If Convert.ToInt32(ds.Tables(LOOKUPS_ENTRY).Rows(l).Item(METRIC_ID)) = metricID Then
                                        Dim lookupID As Integer = Convert.ToInt32(ds.Tables(LOOKUPS_ENTRY).Rows(l).Item(LOOKUPS_ID))
                                        For m As Integer = 0 To ds.Tables(LOOKUP_ENTRY).Rows.Count - 1
                                            If Convert.ToInt32(ds.Tables(LOOKUP_ENTRY).Rows(m).Item(LOOKUPS_ID)) = lookupID Then
                                                metObj.AddLookup(New WMILookup(Convert.ToString(ds.Tables(LOOKUP_ENTRY).Rows(m).Item("Description")), _
                                                                               Convert.ToString(ds.Tables(LOOKUP_ENTRY).Rows(m).Item("Value"))))
                                            End If
                                        Next
                                    End If
                                Next
                                sysMet.AddMetric(metObj)
                            End If
                        Next
                    End If
                Next
                m_entries.Add(sysMet)
            Next

            ds.Dispose()
            ds = Nothing

            'Signal no error.
            m_error = ""
        Catch ex As Exception
            'Keep the error here.
            m_error = ex.ToString()
        End Try

    End Sub

End Class
